#define MSLOG_C
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>    //struct itimerval, setitimer()
#include <time.h>
#include "mslog.h"

#define FLAG "mslog"
//log file
pthread_mutex_t 	mslogfile_mutex;
ms_pbyte  		mslogfile_buf=ms_null;
ms_u32 			mslogfile_buf_maxlen=4096;	
static ms_array 	mslogfile_path[256]={0};
static ms_array 	mslogfile_name[512]={0};
static FILE *		mslogfile_fd =ms_null;

ms_bool  mslog_inner_linefunc=ms_true;
ms_bool  mslog_inner_nor=ms_true;
ms_bool  mslog_inner_file=ms_false;
//local uses
const ms_string astring_loglevel[32]={"assert","error","waring","info","debug","verbose","more","maxlevel"};
static ms_u32 	mslog_level=ENUM_LOG_LEVEL_MAX;
static ms_u32 	mslog_opt=(mslog_level_more|mslog_enable_stdprint|mslog_enable_linefunc);

#define mslog_bits_level(logopt) 		(logopt&0xf)
#define mslog_bits_stdprint(logopt) 	(logopt&mslog_enable_stdprint)
#define mslog_bits_linefunc(logopt) 	(logopt&mslog_enable_linefunc)
#define mslog_bits_filelog(logopt) 	(logopt&mslog_enable_filelog)
#define mslog_bits_timeus(logopt) 	(logopt&mslog_enable_timeus)
static ms_string mslog_innerapi_curdatetime_string(ms_byte *str_curtime,ms_string ms_in frm)
{
	ms_pamcheckret(ms_null, frm, "frm");
	time_t timep=time(ms_null);
	struct tm p_tm;
#if defined(OS_ANDROID)||defined(OS_LINUX_SOC)		
	localtime_r(&timep,&p_tm);
#else
	memcpy(&p_tm,localtime(&timep),sizeof(struct tm));
#endif
	memset(str_curtime, 0, 64);
	snprintf((ms_string)str_curtime,64,frm,
		(p_tm.tm_year+1900),(p_tm.tm_mon+1),(p_tm.tm_mday),(p_tm.tm_hour),(p_tm.tm_min),(p_tm.tm_sec));
	return str_curtime;
}

static ms_u64 mslog_innerapi_us(ms_void)
{
	struct timeval tv;
	gettimeofday(&tv, ms_null);
	return (ms_u64)tv.tv_sec * 1000000 + tv.tv_usec;
}

static ms_void mslog_innerapi_ennor(ms_bool ms_in enable)
{
	mslog_inner_nor=enable;
}
static ms_void mslog_innerapi_enlinefunc(ms_bool ms_in enable)
{
	mslog_inner_linefunc=enable;
}
static ms_void mslog_innerapi_fileopen(ms_void)
{
	if(!mslog_bits_filelog(mslog_opt )){
		return;
	}
	if(ms_false==mslog_inner_file){
		ms_byte str_curtime[64]={0};
		memset(mslogfile_name, 0, sizeof(mslogfile_name));
		if(0!=strlen(mslogfile_path)){
			ms_array cmdline[512]={0};
			snprintf(cmdline,sizeof(cmdline),"mkdir -p %s",mslogfile_path);
			system(cmdline);
			snprintf(mslogfile_name,sizeof(mslogfile_name),"%s/%s.txt",mslogfile_path,mslog_innerapi_curdatetime_string(str_curtime,"%04d%02d%02d_%02d%02d%02d"));
		}else{
			snprintf(mslogfile_name,sizeof(mslogfile_name),"%s.txt",mslog_innerapi_curdatetime_string(str_curtime,"%04d%02d%02d_%02d%02d%02d"));
		}
		mslogfile_fd = fopen(mslogfile_name, "w");
		mslog_inner_file=ms_true;
	}
}
static ms_void mslog_innerapi_fileclose(ms_void)
{
	if(ms_true==mslog_inner_file){
		mslog_inner_file=ms_false;
		fclose(mslogfile_fd);
	}
}
static ms_void mslog_innerapi_setloglevel(ms_u08 ms_in level)
{	
	if(level>ENUM_LOG_LEVEL_MAX || level<mslog_level_assert ){
		mslog_level=mslog_level_error;
		ms_errnoret("Cur loglevel is %s,the set loglevel is %s (Maybe out of range:%s-%s)",
			astring_loglevel[mslog_level],astring_loglevel[level],
			astring_loglevel[mslog_level_assert],astring_loglevel[ENUM_LOG_LEVEL_MAX]);
	}	
	ms_verbose("*Set log level from %s to %s",astring_loglevel[mslog_innerapi_getloglevel()],astring_loglevel[level]);
	mslog_level=level;
}
ms_string mslog_innerapi_num2str( ms_string ms_out outstr, ms_s64 ms_in num)
{
	ms_pamcheckret(ms_null, outstr, "outstr");
	snprintf(outstr, 32, "%"PRId64, num);
	return outstr;
}

ms_string mslog_innerapi_bitbyte64( ms_string ms_out outstr, ms_u64 ms_in num64_h, ms_u64 ms_in num64_l ,ms_string punit)
{
	ms_pamcheckret(ms_null, outstr, "outstr");
	ms_u64 num_100=(num64_l*10)/1024;
	ms_u64 num_010=(((num64_l*10)%1024)*100)/1024;
	if(ms_null==punit){
		snprintf(outstr, 32, "%"PRId64".""%"PRId64"%"PRId64            , num64_h,num_100,num_010);
	}else{
		snprintf(outstr, 32, "%"PRId64".""%"PRId64"%"PRId64 " %s", num64_h,num_100,num_010,punit);
	}
	return outstr;
}

ms_string mslog_innerapi_bitbyte32( ms_string ms_out outstr, ms_u32 ms_in num32_h, ms_u32 ms_in num32_l,ms_string punit)
{
	ms_pamcheckret(ms_null, outstr, "outstr");
	ms_u32 num_100=(num32_l*10)/1024;
	ms_u32 num_010=(((num32_l*10)%1024)*100)/1024;
	if(ms_null==punit){
		snprintf(outstr, 32, "%u.%u%u       ", num32_h,num_100,num_010);
	}else{
		snprintf(outstr, 32, "%u.%u%u %s", num32_h,num_100,num_010,punit);
	}
	return outstr;
}
ms_string mslog_innerapi_systimeus(ms_byte *str_curtime)
{
	static ms_u64 log_time_base=0;
	if(0==log_time_base){
		log_time_base=mslog_innerapi_us();
	}
	ms_u64 log_time_cur=mslog_innerapi_us()-log_time_base;
	memset(str_curtime, 0, 64);
	snprintf((ms_string)str_curtime,64,"%ld",log_time_cur);
	return str_curtime;
}

ms_string mslog_innerapi_systimedate_filelog(ms_void)
{	
	static ms_byte str_curtime[64]={0};
	if(!mslog_bits_timeus(mslog_opt )){
		return mslog_innerapi_curdatetime_string(str_curtime,"%04d-%02d-%02d %02d:%02d:%02d");
	}else{
		return mslog_innerapi_systimeus(str_curtime);
	}
}
ms_string mslog_innerapi_systimedate_nor(ms_void)
{	
	static ms_byte str_curtime[64]={0};
	if(!mslog_bits_timeus(mslog_opt )){
		return mslog_innerapi_curdatetime_string(str_curtime,"%04d-%02d-%02d %02d:%02d:%02d");
	}else{
		return mslog_innerapi_systimeus(str_curtime);
	}
}
ms_bool mslog_api_counter_async(time_t * ms_in Etimep,ms_u32 ms_in sec)	//s
{
	time_t timep=time(ms_null);
	if(timep>((*Etimep)+sec)){
		(*Etimep)=timep;
		return ms_true;
	}else{
		return ms_false;
	}
} 
ms_void mslog_innerapi_filewrite(ms_pu08 ms_in buf, ms_u32 ms_in size)
{
	if(mslog_inner_file){
		ms_pamcheck(buf, "buf");
		static time_t timep_w=0;
		if(ms_true==mslog_api_counter_async(&timep_w,1)){
			if (access(mslogfile_name,F_OK) < 0){
				mslog_innerapi_fileclose();
				mslog_innerapi_fileopen();
			}
		}
		fwrite(buf, sizeof(char), size, mslogfile_fd);
	}
}
ms_void mslog_innerapi_buf( ms_string ms_in description,ms_pu08 ms_in buf, ms_u32 ms_in len,ms_cstring ms_in func,ms_u32 ms_in line)
{
	#define num_perkline 16
	ms_pamcheck( description, "description");
	ms_pamcheck( buf, "buf");
	
	ms_debug(">>>%s<<<---len:%d,num_perkline:%d,in func %s,line %d",description,len,num_perkline,func,line);
	
	ms_u32  lineindex = 0;
	for (lineindex=0; lineindex < len; lineindex+=num_perkline	){
		if ( (len-lineindex) <num_perkline	){
			unsigned char tempbuf[num_perkline]={0};
			memcpy(tempbuf, &buf[lineindex], (len-lineindex)	);
			ms_debug( "[%08x:%08x]%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x ",
			lineindex,(lineindex+0x0f),
			tempbuf[0x00] ,tempbuf[0x01] ,tempbuf[0x02] ,tempbuf[0x03] ,tempbuf[0x04] ,tempbuf[0x05] ,tempbuf[0x06] ,tempbuf[0x07] ,
			tempbuf[0x08] ,tempbuf[0x09] ,tempbuf[0x0a] ,tempbuf[0x0b] ,tempbuf[0x0c] ,tempbuf[0x0d] ,tempbuf[0x0e] ,tempbuf[0x0f] );		
		}else{
			ms_debug( "[%08x:%08x]%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x ",
			lineindex,(lineindex+0x0f),
			buf[lineindex+0x00] ,buf[lineindex+0x01] ,buf[lineindex+0x02] ,buf[lineindex+0x03] ,buf[lineindex+0x04] ,buf[lineindex+0x05] ,buf[lineindex+0x06] ,buf[lineindex+0x07] ,
			buf[lineindex+0x08] ,buf[lineindex+0x09] ,buf[lineindex+0x0a] ,buf[lineindex+0x0b] ,buf[lineindex+0x0c] ,buf[lineindex+0x0d] ,buf[lineindex+0x0e] ,buf[lineindex+0x0f] );
		}
	}
}

ms_void mslog_innerapi_bufandascii( ms_string ms_in description,ms_pu08 ms_in buf, ms_u32 ms_in len,ms_cstring ms_in func,ms_u32 ms_in line)
{
	#define num_perkline 16
	ms_pamcheck( description, "description");
	ms_pamcheck( buf, "buf");
	
	ms_debug(">>>%s<<<---len:%d,num_perkline:%d,in func %s,line %d",description,len,num_perkline,func,line);
	
	ms_u32  lineindex = 0;
	for (lineindex=0; lineindex < len; lineindex+=num_perkline	){
		if ( (len-lineindex) <num_perkline	){
			unsigned char tempbuf[num_perkline]={0};
			memcpy(tempbuf, &buf[lineindex], (len-lineindex)	);
			ms_debug( "[%08x:%08x]%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x \t%c %c %c %c %c %c %c %c %c %c %c %c %c %c %c %c",
			lineindex,(lineindex+0x0f),
			tempbuf[0x00] ,tempbuf[0x01] ,tempbuf[0x02] ,tempbuf[0x03] ,tempbuf[0x04] ,tempbuf[0x05] ,tempbuf[0x06] ,tempbuf[0x07] ,
			tempbuf[0x08] ,tempbuf[0x09] ,tempbuf[0x0a] ,tempbuf[0x0b] ,tempbuf[0x0c] ,tempbuf[0x0d] ,tempbuf[0x0e] ,tempbuf[0x0f] ,
			tempbuf[0x00] ,tempbuf[0x01] ,tempbuf[0x02] ,tempbuf[0x03] ,tempbuf[0x04] ,tempbuf[0x05] ,tempbuf[0x06] ,tempbuf[0x07] ,
			tempbuf[0x08] ,tempbuf[0x09] ,tempbuf[0x0a] ,tempbuf[0x0b] ,tempbuf[0x0c] ,tempbuf[0x0d] ,tempbuf[0x0e] ,tempbuf[0x0f] );		
		}else{
			ms_debug( "[%08x:%08x]%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x \t%c %c %c %c %c %c %c %c %c %c %c %c %c %c %c %c",
			lineindex,(lineindex+0x0f),
			buf[lineindex+0x00] ,buf[lineindex+0x01] ,buf[lineindex+0x02] ,buf[lineindex+0x03] ,buf[lineindex+0x04] ,buf[lineindex+0x05] ,buf[lineindex+0x06] ,buf[lineindex+0x07] ,
			buf[lineindex+0x08] ,buf[lineindex+0x09] ,buf[lineindex+0x0a] ,buf[lineindex+0x0b] ,buf[lineindex+0x0c] ,buf[lineindex+0x0d] ,buf[lineindex+0x0e] ,buf[lineindex+0x0f],
			buf[lineindex+0x00] ,buf[lineindex+0x01] ,buf[lineindex+0x02] ,buf[lineindex+0x03] ,buf[lineindex+0x04] ,buf[lineindex+0x05] ,buf[lineindex+0x06] ,buf[lineindex+0x07] ,
			buf[lineindex+0x08] ,buf[lineindex+0x09] ,buf[lineindex+0x0a] ,buf[lineindex+0x0b] ,buf[lineindex+0x0c] ,buf[lineindex+0x0d] ,buf[lineindex+0x0e] ,buf[lineindex+0x0f] );
		}
	}
}

ms_void mslog_innerapi_errbuf( ms_string ms_in description,ms_pu08 ms_in buf, ms_u32 ms_in len,ms_cstring ms_in func,ms_u32 ms_in line)
{
	#define num_perkline 16
	ms_pamcheck( description, "description");
	ms_pamcheck( buf, "buf");
	
	ms_error(">>>%s<<<---len:%d,num_perkline:%d,in func %s,line %d",description,len,num_perkline,func,line);
	
	ms_u32  lineindex = 0;
	for (lineindex=0; lineindex < len; lineindex+=num_perkline	){
		if ( (len-lineindex) <num_perkline	){
			unsigned char tempbuf[num_perkline]={0};
			memcpy(tempbuf, &buf[lineindex], (len-lineindex)	);
			ms_error( "[%08x:%08x]%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x ",
			lineindex,(lineindex+0x0f),
			tempbuf[0x00] ,tempbuf[0x01] ,tempbuf[0x02] ,tempbuf[0x03] ,tempbuf[0x04] ,tempbuf[0x05] ,tempbuf[0x06] ,tempbuf[0x07] ,
			tempbuf[0x08] ,tempbuf[0x09] ,tempbuf[0x0a] ,tempbuf[0x0b] ,tempbuf[0x0c] ,tempbuf[0x0d] ,tempbuf[0x0e] ,tempbuf[0x0f] );		
		}else{
			ms_error( "[%08x:%08x]%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x ",
			lineindex,(lineindex+0x0f),
			buf[lineindex+0x00] ,buf[lineindex+0x01] ,buf[lineindex+0x02] ,buf[lineindex+0x03] ,buf[lineindex+0x04] ,buf[lineindex+0x05] ,buf[lineindex+0x06] ,buf[lineindex+0x07] ,
			buf[lineindex+0x08] ,buf[lineindex+0x09] ,buf[lineindex+0x0a] ,buf[lineindex+0x0b] ,buf[lineindex+0x0c] ,buf[lineindex+0x0d] ,buf[lineindex+0x0e] ,buf[lineindex+0x0f] );
		}
	}
}

ms_void mslog_innerapi_bufascii( ms_string ms_in description,ms_pu08 ms_in buf, ms_u32 ms_in len,ms_cstring ms_in func,ms_u32 ms_in line)
{
	#define num_perkline 16
	ms_pamcheck( description, "description");
	ms_pamcheck( buf, "buf");
	
	ms_debug(">>>%s<<<---len:%d,num_perkline:%d,in func %s,line %d",description,len,num_perkline,func,line);
	
	ms_u32  lineindex = 0;
	for (lineindex=0; lineindex < len; lineindex+=num_perkline	){
		if ( (len-lineindex) <num_perkline	){
			unsigned char tempbuf[num_perkline]={0};
			memcpy(tempbuf, &buf[lineindex], (len-lineindex)	);
			ms_debug( "[%08x:%08x]%c %c %c %c %c %c %c %c %c %c %c %c %c %c %c %c",
			lineindex,(lineindex+0x0f),
			tempbuf[0x00] ,tempbuf[0x01] ,tempbuf[0x02] ,tempbuf[0x03] ,tempbuf[0x04] ,tempbuf[0x05] ,tempbuf[0x06] ,tempbuf[0x07] ,
			tempbuf[0x08] ,tempbuf[0x09] ,tempbuf[0x0a] ,tempbuf[0x0b] ,tempbuf[0x0c] ,tempbuf[0x0d] ,tempbuf[0x0e] ,tempbuf[0x0f] );		
		}else{
			ms_debug( "[%08x:%08x]%c %c %c %c %c %c %c %c %c %c %c %c %c %c %c %c",
			lineindex,(lineindex+0x0f),
			buf[lineindex+0x00] ,buf[lineindex+0x01] ,buf[lineindex+0x02] ,buf[lineindex+0x03] ,buf[lineindex+0x04] ,buf[lineindex+0x05] ,buf[lineindex+0x06] ,buf[lineindex+0x07] ,
			buf[lineindex+0x08] ,buf[lineindex+0x09] ,buf[lineindex+0x0a] ,buf[lineindex+0x0b] ,buf[lineindex+0x0c] ,buf[lineindex+0x0d] ,buf[lineindex+0x0e] ,buf[lineindex+0x0f] );
		}
	}
}

ms_u32 mslog_innerapi_getloglevel(ms_void)
{
	return mslog_level;
}

ms_void mslog_api_setopt(ms_u32 ms_in logopt,ms_string ms_in dir,ms_u32 maxlen)
{
	mslog_opt=logopt;
	mslog_innerapi_setloglevel((ms_u08)mslog_bits_level(logopt) );

	memset(mslogfile_path, 0, sizeof(mslogfile_path));
	if(ms_null!=dir){	
		strcpy(mslogfile_path,dir);
	}else{
		strcpy(mslogfile_path,"/tmp");
	}
	if(mslog_bits_stdprint(logopt)){
		mslog_innerapi_ennor(ms_true);
		ms_verbose("Open the function of printing stdin and stdout");
	}else{
		mslog_innerapi_ennor(ms_false);
	}	
	
	if(mslog_bits_linefunc(logopt )){
		mslog_innerapi_enlinefunc(ms_true);
		ms_verbose("Open the function of printing line and funcname");
	}else{
		mslog_innerapi_enlinefunc(ms_false);
	}
	if(mslog_bits_filelog(logopt )){
		if(0!=maxlen){
			mslogfile_buf_maxlen=maxlen;
		}
		if(ms_null==mslogfile_buf){
			mslogfile_buf=malloc(mslogfile_buf_maxlen);
			if(ms_null==mslogfile_buf){
				mslog_inner_file=ms_false;
				ms_errnoret("Malloc mslogfile_buf failed,cannot printing log to file");
			}else{
				mslog_innerapi_fileopen();
				int ret = pthread_mutex_init(&mslogfile_mutex, ms_null);
				if (ret != 0) {
					ms_error( "pthread_mutex_init failed : %s", strerror(ret));
				}
				ms_verbose("Open the function of printing log to file");
			}
		}
	}	
	if(mslog_bits_timeus(logopt )){
		ms_verbose("Open the function of printing us time");
	}
}

ms_void mslog_api_getopt_string(ms_u32 ms_in logopt,ms_string ms_in outstr)
{
	sprintf(outstr, "%s  | dir:%s  |  %s  |  %s   |  %s|  %s", astring_loglevel[mslog_innerapi_getloglevel()],mslogfile_path,
		(mslog_bits_stdprint(logopt)) 	? "enable_stdprint" 	: "disable_stdprint",
		(mslog_bits_linefunc(logopt)) 	? "enable_linefunc" 	: "disable_linefunc",
		(mslog_bits_filelog(logopt)) 	? "enable_filelog" 	: "disable_filelog",
		(mslog_bits_timeus(logopt)) 	? "enable_timeus" 	: "disable_timeus"
	);
}
ms_string mslog_api_getlogfile(ms_void)
{
	return mslogfile_name;
}
ms_void mslog_api_unsetopt(ms_void)
{
	ms_u32 logopt=mslog_opt;
	if(mslog_bits_stdprint(logopt )){
		mslog_innerapi_ennor(ms_false);
		ms_debug("Close the function of printing stdin and stdout");
	}	
	if(mslog_bits_linefunc(logopt )){
		mslog_innerapi_enlinefunc(ms_false);
		ms_debug("Close the function of printing line and funcname");
	}	
	if(mslog_bits_filelog(logopt )){
		if(ms_null!=mslogfile_buf){
			mslog_innerapi_fileclose();
			ms_debug("Close the function of printing log to file");
			free(mslogfile_buf);
			mslogfile_buf=ms_null;
			pthread_mutex_destroy(&mslogfile_mutex);
		}
	}	
}
ms_void mslog_api_new(ms_void)
{
	mslog_innerapi_fileclose();
	mslog_innerapi_fileopen();
}
ms_void mslog_api_infofunc(ms_string ms_out pbuf)
{
#if defined OS_LINUX_SOC  
	strcat(pbuf, "linux_soc");
#else
	strcat(pbuf, "unlinux_soc");
#endif
	strcat(pbuf, "|all-func");
}
ms_string mslog_api_version(ms_void)
{
	return "02.00.20";
}
#undef MSLOG_C
